package com.gonsin.gmessage.logic.proxy;

import com.gonsin.gmessage.annotations.GArgsBody;
import com.gonsin.gmessage.annotations.GArgsParam;
import com.gonsin.gmessage.annotations.GSender;
import com.gonsin.gmessage.annotations.GZoom;
import com.gonsin.gmessage.annotations.enums.ZoomDescribe;
import com.gonsin.gmessage.logic.exceptions.GMessageException;
import org.springframework.util.CollectionUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * GSender方法，参数结构描述
 */
public class ParameterDescriberModel {


    /**
     * 哪个参数被GZoom标注
     */
    private int zoomIndex = -1;

    /**
     * 是否为全域发送
     */
    private boolean zoomAll;

    /**
     * Args参数描述
     */
    private List<ArgsDescribeModel> argsDescribeModels;

    /**
     * body描述
     */
    private ArgsBodyDescribeModel argsBodyDescribeModel;


    /**
     * 配置参数
     */
    private List<IArgsPush> argsPusher;


    public ParameterDescriberModel(GSender sender, Method method){
        Annotation[][] annotations = method.getParameterAnnotations();

        argsDescribeModels = new ArrayList<>();
        argsPusher = new ArrayList<>();
        for(int i = 0; i < annotations.length; i++){

            if(annotations[i] == null || annotations[i].length == 0){
                throw new GMessageException("GSender批注的方法中，有且只能有1个批注");
            }

            GArgsParam args = findGArgs(annotations[i]);
            if(args != null){

                if(argsBodyDescribeModel != null){
                    throw new GMessageException("GSender批注的方法中，@GArgsBody 和 @GArgsParam 不能共用");
                }

                Class<?> rowType = method.getParameterTypes()[i];
                ArgsDescribeModel model = new ArgsDescribeModel(args, i, rowType);
                argsDescribeModels.add(model);
                argsPusher.add(model);

                if(zoomIndex == -1){
                    GZoom zoom = findGZoom(annotations[i]);
                    if(zoom != null){
                        zoomIndex = i;
                    }
                } else {
                    GZoom zoom = findGZoom(annotations[i]);
                    if(zoom != null){
                        throw new GMessageException("GSender批注的方法中，最多只能有一个@GZoom");
                    }
                }

                continue;
            }

            GZoom zoom = findGZoom(annotations[i]);
            if(zoom != null){
                if(zoomIndex == -1){
                    zoomIndex = i;
                    argsPusher.add(new NonPush());
                }
                continue;
            }

            GArgsBody body = findGArgsBody(annotations[i]);
            if(body != null){

                if(argsBodyDescribeModel != null){
                    throw new GMessageException("GSender批注的方法中，只能有一个@GArgsBody");
                }
                if(!CollectionUtils.isEmpty(argsDescribeModels)){
                    throw new GMessageException("GSender批注的方法中，@GArgsBody 和 @GArgsParam 不能共用");
                }
                Class<?> rowType = method.getParameterTypes()[i];
                ArgsBodyDescribeModel model = new ArgsBodyDescribeModel(body, i, rowType);
                argsBodyDescribeModel = model;
                argsPusher.add(model);
                continue;
            }

            throw new GMessageException("GSender批注的方法，参数至少要包含 @GZoom、@GArgsParam @ArgsBody其中之一");
        }


        if(zoomIndex == -1){
            if(sender.zoomDescribe() != ZoomDescribe.ALL){
                throw new GMessageException("@GSender批注的方法如果发送域为【other】，则必须包含GZoom，用于表示发送的域，且参数类型必须为String。" +
                        "你可以在方法参数中添加 【@GZoom String zoom】 解决；或在@GSender中修改  zoomDescribe = ALL");
            } else {
                zoomAll = true;
            }
        }
        if(zoomIndex != -1){
            if(!String.class.isAssignableFrom(method.getParameterTypes()[zoomIndex])){
                throw new GMessageException("@GZoom标注的参数类型必须为字符串String。");
            }
        }
    }

    private static GZoom findGZoom(Annotation[] annotation) {
        if(annotation[0] instanceof GZoom){
            return (GZoom) annotation[0];
        }
        return null;
    }

    private static GArgsBody findGArgsBody(Annotation[] annotation) {
        if(annotation[0] instanceof GArgsBody){
            return (GArgsBody) annotation[0];
        }
        return null;
    }

    /**
     * 查找GArgs批注
     */
    private static GArgsParam findGArgs(Annotation[] annotation) {
        if(annotation[0] instanceof GArgsParam){
            return (GArgsParam) annotation[0];
        }
        return null;
    }

    /**
     * zoom所在的位置
     */
    public int getZoomIndex() {
        return zoomIndex;
    }

    /**
     * 设置参数
     */
    public void pushArgs(Map<String, Object> args, Object argument, int i) {
        argsPusher.get(i).put(args, argument);
    }


    /**
     * 是否为全部域
     */
    public boolean isZoomAll() {
        return zoomAll;
    }
}
